# Copyright (C) 2003-2004, 2006, 2009 The Written Word, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# $Id: ftp.py 694 2010-02-19 14:55:45Z china $

import urllib2

from urlparse import urlparse

import depot
import pycurl

from depot.http import http
from misclib import urljoin


class ftp (http):
  """Remote package `Depot` over FTP."""

  def __init__ (self, var, local_depot, dep, sub_depot):
    """Return an FTP `Depot` object."""

    # This is better, but doesn't work for classic classes:
    #    super (ftp, self).__init__ (var, local_depot, dep, sub_depot)
    # So, we're forced to hardcode the superclass for now:
    http.__init__ (self, var, local_depot, dep, sub_depot)

    urltuple = urlparse (self.url, 'ftp')
    [self.host, self.port] = (urltuple.hostname, urltuple.port or 21)

  def _perform (self, options):
    """An option-setting  wrapper for `pycurl`.

    :Parameters:
        `options`: dictionary
            A dictionary of PYCURL option names and settings. 

    :Exceptions:
         - `DepotFileNotFoundError`: If a remote file is not found.
         - `DepotAccessDeniedError`: If remote authentication failed.
         - `DepotAccessError`: Any other problem fetching.

    Instantiate pycurl and use it to perform an action based on the
    passed `options`.
    """
    c = pycurl.Curl ()

    #c.setopt (pycurl.VERBOSE, 1)

    for (k, v) in options.items ():
      c.setopt (k, v)

    self._proxy_auth (c)
    self._protocol_auth (c)

    try:
      c.perform ()
    except pycurl.error, msg:
      status = msg[0]
      if status in (pycurl.E_FTP_COULDNT_RETR_FILE,
                    pycurl.E_REMOTE_FILE_NOT_FOUND):
        raise depot.DepotFileNotFoundError (self.url)
      elif status in (pycurl.E_LOGIN_DENIED, pycurl.E_FTP_ACCESS_DENIED):
        raise depot.DepotAccessDeniedError
      else:
        raise depot.DepotAccessError (msg[1])

  def _protocol_auth (self, c):
    """Handle FTP specific authentication."""
    if self.auth.login:
      c.setopt (pycurl.USERPWD, self.auth.login + ':' + \
                (self.auth.passwd or ''))

    # XXX ftps not tested (or previously supported)
    c.setopt (pycurl.FTP_SSL, pycurl.FTPSSL_TRY)
    c.setopt (pycurl.FTPSSLAUTH, pycurl.FTPAUTH_TLS)
    
  def _proxy_auth (self, c):
    """Handle proxy configuration."""
    if self.proxy.host:
      c.setopt (pycurl.PROXY, self.proxy.host)
      c.setopt (pycurl.PROXYTYPE, pycurl.PROXYTYPE_HTTP)
      c.setopt (pycurl.PROXYAUTH, pycurl.HTTPAUTH_ANY)
    if self.proxy.login:
      c.setopt (pycurl.PROXYUSERPWD, self.proxy.login + ':' + \
                (self.proxy.passwd or ''))

  def exists (self, filename):
    """Test whether `filename` is present at the remote host.

    :Parameters:
        `filename`: string
            Path to remote file relative to remote repository.

    :Return:
        - True unless remote file cannot be found.
    """

    # save full url in case of error
    _url = urljoin (self.pkg_url, filename)

    try:
      self._perform ({
        pycurl.URL: _url,
        pycurl.WRITEDATA: open ('/dev/null', 'wb'),  # discard headers
        pycurl.OPT_FILETIME: 1,
        pycurl.NOBODY: 1,
        pycurl.NOPROGRESS: 1,
        pycurl.FAILONERROR: 1,
      })
    except depot.DepotFileNotFoundError:
      return False

    return True

  def set_proxy (self):
    """Set proxy information.

    Initiate ftp connection to proxy server with login@host as username.
    """

    if not self.proxy.host:
      return

    if self.auth.login:
      self.auth.login = self.auth.login + '@' + self.host + ':' + \
                        str (self.port)
    else:
      self.auth.login = 'anonymous@' + self.host + ':' + str (self.port)

    proxytuple = urllib2.splitport (self.proxy.host)
    [self.host, self.port] = (proxytuple[0], int (proxytuple[1] or 21))
